package com.rslai.commons.http.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.regex.Pattern;

import org.apache.http.HttpEntity;
import org.apache.http.ParseException;
import org.apache.http.util.CharArrayBuffer;
import org.apache.http.util.EntityUtils;

/**
 * string工具类
 */
public class StringUtil {
    private static final String regExp1 = "^([\\x01-\\x7f]|[\\xc0-\\xdf][\\xa0-\\xbf])+$";
    private static final String regExp2 = "^([\\x01-\\x7f]|[\\xc0-\\xdf][\\x80-\\xbf]|[\\xe0-\\xef][\\x80-\\xbf]{2}|[\\xf0-\\xf7][\\x80-\\xbf]{3}|[\\xf8-\\xfb][\\x80-\\xbf]{4}|[\\xfc-\\xfd][\\x80-\\xbf]{5})+$";
    private static final Pattern REGEXP1_PATTERN = Pattern.compile(regExp1);
    private static final Pattern REGEXP2_PATTERN = Pattern.compile(regExp2);
    private static final byte[] UTF8Signature = new byte[] { -17, -69, -65 }; // EF BB BF

    /**
     * 检查传入的 buf 字节数组，是否以 prefix 字节数组开始
     * @param buf 待检测时字节数组
     * @param offset 偏移量
     * @param prefix 匹配的内容
     * @return true：是以 prefix 字节数组开始，false：不是以 prefix 字节数组开始
     */
    private static boolean startsWith(byte[] buf, int offset, byte[] prefix) {
        for (int i = 0; i < prefix.length; i++) {
            if (i + offset == buf.length || buf[i + offset] != prefix[i]) {
                return false;
            }
        }
        return true;
    }

    /**
     * 检查字节数组是否是 utf8 编码
     * @param buf 待检测时字节数组
     * @param offset 偏移量
     * @param length 检查的长度
     * @return true：是 utf8 编码，false：不是 utf8 编码
    */
    public static boolean isUTF8(byte[] buf, int offset, int length) {
        if (startsWith(buf, offset, UTF8Signature))
            return true;
        boolean utf8 = false;
        for (int i = offset; i < offset + length; i++) {
            if ((buf[i] & 0xC0) == 0xC0) { // 以 0b11xxxxxx 开始
                // 按位查是否为一个 utf8 字符
                int nBytes;
                for (nBytes = 2; nBytes < 8; nBytes++) {
                    int mask = 1 << (7 - nBytes);
                    if ((buf[i] & mask) == 0)
                        break;
                }
                // 检查以 0B10XXXXXX 开始
                for (int j = 1; j < nBytes; j++) {
                    if (i + j >= length || (buf[i + j] & 0xC0) != 0x80)
                        return false;
                }
                utf8 = true;
            }
        }
        return utf8;
    }

    /**
     * 检查字符串是否是 utf8 编码
     * @param str 待检测的字符串
     * @return true：是 utf8 编码，false：不是 utf8 编码
     */
    public static boolean isUTF8(String str) {
        boolean match = REGEXP1_PATTERN.matcher(str).matches();
        if (match) {
            return false;
        }
        match = REGEXP2_PATTERN.matcher(str).matches();
        if (match) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 将 HttpEntity 转为 defaultCharset 编码
     * @param entity HttpEntity
     * @param defaultCharset defaultCharset
     * @return 字符串
     * @throws IOException IOException
     * @throws ParseException ParseException
     */
    public static String toString(final HttpEntity entity, final String defaultCharset) throws IOException, ParseException {
        if (entity == null) {
            throw new IllegalArgumentException("HTTP entity may not be null");
        }
        InputStream instream = entity.getContent();
        if (instream == null) {
            return null;
        }
        if (entity.getContentLength() > Integer.MAX_VALUE) {
            throw new IllegalArgumentException("HTTP entity too large to be buffered in memory");
        }
        int i = (int) entity.getContentLength();
        if (i < 0) {
            i = 4096;
        }
        String charset = EntityUtils.getContentCharSet(entity);
        if (charset == null) {
            charset = defaultCharset;
        }

        if (charset != null) {
            return readFromHttp(instream, charset, i);
        } else {
            return readFromHttp(instream, i);
        }
    }

    /**
     * 将 HttpEntity 转为 byte[] 数组
     * @param entity HttpEntity
     * @return byte数组
     * @throws IOException IOException
     * @throws ParseException ParseException
     */
    public static byte[] toBytes(final HttpEntity entity) throws IOException, ParseException {
        if (entity == null) {
            throw new IllegalArgumentException("HTTP entity may not be null");
        }
        InputStream instream = entity.getContent();
        if (instream == null) {
            return null;
        }
        if (entity.getContentLength() > Integer.MAX_VALUE) {
            throw new IllegalArgumentException("HTTP entity too large to be buffered in memory");
        }
        int i = (int) entity.getContentLength();
        if (i < 0) {
            i = 4096;
        }
        ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream(i);
        int c;
        while ((c = instream.read()) != -1) {
            arrayOutputStream.write(c);
        }
        byte[] data = arrayOutputStream.toByteArray();
        return data;
    }

    /**
     * 从 http 流中读入数据（指定 charset）
     * @param instream InputStream
     * @param charset 字符集
     * @param bufferLen buffer 长度
     * @return 字符串
     * @throws IOException IOException
     */
    private static String readFromHttp(InputStream instream, String charset, int bufferLen) throws IOException {
        Reader reader = new InputStreamReader(instream, charset);
        CharArrayBuffer buffer = new CharArrayBuffer(bufferLen);
        try {
            char[] tmp = new char[1024];
            int l;
            while ((l = reader.read(tmp)) != -1) {
                buffer.append(tmp, 0, l);
            }
        } finally {
            reader.close();
        }
        return buffer.toString();
    }

    /**
     * 从 http 流中读入数据（未指定 charset，需要检测）
     * @param instream InputStream
     * @param bufferLen buffer 长度
     * @return 字符串
     * @throws IOException IOException
     */
    private static String readFromHttp(InputStream instream, int bufferLen) throws IOException {
        ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream(8192);
        int c;
        while ((c = instream.read()) != -1) {
            arrayOutputStream.write(c);
        }
        byte[] data = arrayOutputStream.toByteArray();
        String html = new String(data, "UTF-8");
        if (!StringUtil.isUTF8(data, 0, data.length)) {
            html = new String(data, "GBK");
        }
        return html;
    }

}
